#ifndef G3_INPUT_H
#define G3_INPUT_H

#include <SDL/SDL.h>

#include "math/m_vector.h"
#include "render/r_view.h"
#include "timer.h"

#include <stdlib.h>

#include <boost/signal.hpp>
#include <boost/signals/connection.hpp>

// Normal mouse mode (no centering and {0, 0} is the upper left of the window)
#define MOUSE_NORMAL               0
// The FPS mouse mode
#define MOUSE_CENTER               1
// Default mouse sensitivity
#define MOUSE_DEF_SENSITIVITY      1000.0

#define MOUSE_WHEEL_SENSITIVITY    3

namespace G3 {

class KEYBOARD {
	public:
		KEYBOARD ();

		byte *KeyDown;
		int Modifiers;
		int NumKeys;

		void Update ();
		bool IsKeyDown (int KeyID);
};

class MOUSE {
    public:
        MOUSE ();

        VEC3I LastPosition;
        VEC3I Position;

        VEC3I ScreenCenter;

        uint Mode;
        int ScreenWidth;
        int ScreenHeight;

		uint State;

        double Sensitivity;

//==============================================================================
// Sets mouse mode - normal or centered for a FPS camera
//==============================================================================
        void SetMode (unsigned int NewMode);

//==============================================================================
// Sets the screen size - needed for the FPS mouse mode
//==============================================================================
        void SetScreenSize (int Width, int Height);

//==============================================================================
// Positions the mouse
//==============================================================================
        void SetPosition (int x, int y, int z = 0);

//==============================================================================
// Gets relative mouse position
//==============================================================================
        VEC3I GetPosition ();

//==============================================================================
// Positions mouse at the center of the screen
//==============================================================================
        void Center ();

//==============================================================================
// Updates the mouse position
//==============================================================================
        VEC3I UpdatePosition ();

//==============================================================================
// Returns Y and Z deltas for updating the FPS camera
//==============================================================================
        VEC3F GetDeltas ();

//==============================================================================
// Rotates the given camera with our mouse
//==============================================================================
        void MouseLook (VIEW *cam);
};

class INPUT {
    public:
        INPUT (class FRAME *ParentFrame = NULL);

        ~INPUT ();

//==============================================================================
// Poll SDL input device events and call appropriate handlers and callbacks
//==============================================================================
        void GetInput ();

//==============================================================================
// Sends a Quit message to SDL and shuts down the engine
//==============================================================================
		void SendQuit ();

        SDL_Event event;
		TIMER Timer;
		float ElapsedTime;
		float LastElapsedTime;

        class FRAME *Frame;

        MOUSE Mouse;
		KEYBOARD Keyboard;

		typedef boost::signal0<void> SIG_EXIT;
		typedef boost::signal3<void, INPUT *, SDL_keysym *, float> SIG_KEY_EVENT;
		typedef boost::signal3<void, INPUT *, SDL_Event *, float> SIG_MOUSE_EVENT;
		typedef boost::signal4<void, INPUT *, SDL_Event *, float, float> SIG_WHEEL_EVENT;
		typedef boost::signal2<void, INPUT *, float> SIG_UPDATE_EVENT;
		typedef boost::signals::connection SIG_CONNECTION;

		typedef void (*EXIT_HANDLER)(void);
		typedef void (*KEY_HANDLER)(INPUT *Input, SDL_keysym* keysym, float aElapsedTime);
		typedef void (*MOUSE_HANDLER)(INPUT *Input, SDL_Event* Event, float aElapsedTime);
		typedef void (*WHEEL_HANDLER)(INPUT *Input, SDL_Event* Event, float aWheel, float aElapsedTime);
		typedef void (*UPDATE_HANDLER)(INPUT *Input, float aElapsedTime);

		// Signals
		SIG_EXIT SigExit;
		SIG_KEY_EVENT SigKeyDown;
		SIG_KEY_EVENT SigKeyUp;
		SIG_MOUSE_EVENT SigMouseDown;
		SIG_MOUSE_EVENT SigMouseUp;
		SIG_MOUSE_EVENT SigMouseMove;
        SIG_WHEEL_EVENT SigMouseWheel;
		SIG_UPDATE_EVENT SigUpdate;

		// Connections
		vector<SIG_CONNECTION> ConnExit;
		vector<SIG_CONNECTION> ConnKeyDown;
		vector<SIG_CONNECTION> ConnKeyUp;
		vector<SIG_CONNECTION> ConnMouseDown;
		vector<SIG_CONNECTION> ConnMouseUp;
		vector<SIG_CONNECTION> ConnMouseMove;
		vector<SIG_CONNECTION> ConnMouseWheel;
		vector<SIG_CONNECTION> ConnUpdate;

		// Wrapper functions
		SIG_CONNECTION doOnExit (EXIT_HANDLER aExitHandler);
		SIG_CONNECTION doOnKeyDown (KEY_HANDLER aKeyHandler);
		SIG_CONNECTION doOnKeyUp (KEY_HANDLER aKeyHandler);
		SIG_CONNECTION doOnMouseDown (MOUSE_HANDLER aMouseHandler);
		SIG_CONNECTION doOnMouseUp (MOUSE_HANDLER aMouseHandler);
		SIG_CONNECTION doOnMouseMove (MOUSE_HANDLER aMouseHandler);
		SIG_CONNECTION doOnMouseWheel (WHEEL_HANDLER aWheelHandler);
		SIG_CONNECTION doOnUpdate (UPDATE_HANDLER aUpdateHandler);

//==============================================================================
// Gets the elapsed time
//==============================================================================
		float GetElapsedTime ();
};

//==============================================================================
// The default keyhandler callback - called when none are specified
//==============================================================================
extern void DefaultUpdateHandler (INPUT *Input, float aElapsedTime);

}

#endif //G3_INPUT_H
